# -*- coding: utf-8 -*-
"""
#
# Authors: kenny huang
# OsChina: http://my.oschina.net/pydevops/
# Purpose:
#
"""
from constant import *
from logger import logger
import json
from datetime import datetime as dt_date_time
import base64
import traceback
import threading

import requests
from requests.auth import HTTPBasicAuth
from requests import Request, Session
import urllib3
from unpack import UnPackData

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

import logging
logging.getLogger("requests").setLevel(logging.WARNING)
logging.getLogger("urllib3").setLevel(logging.WARNING)

def checkParam(strParam):
    if len(strParam) > 0:
        return True
    return False

class NBiotClient(object):
    def __init__(self, strIp, strPort, strAppId, strPassword):

        self.mStrBaseUrl = "https://" + strIp + ":" + strPort + "/"
        self.mStrAppId = strAppId
        self.mStrPassword = strPassword
        self.mAuthTime = dt_date_time.now()
        self.nbiot_sendlock = threading.RLock()

        self.mMapHeader = {}
        self.mStrRefreshToken = ''
        self.mRefreshTokenTimeout = 0
        self.httpsclient = requests.Session()
        self.httpsclient.cert = (NBIOT_SERVER_CA_FILE, NBIOT_SERVER_CA_KEY)
        self.httpsclient.verify = False
        logger.info("NBIot server %s" % (self.mStrBaseUrl))


    def doPostFormUrlEncodedForString(self, strUrl, mParam):
        try:
            if checkParam(strUrl) == False and checkParam(mParam) == False:
                return{}

            headers = {'content-type': 'application/x-www-form-urlencoded'}
            response = self.httpsclient.post(url=strUrl, data=mParam, headers = headers)
            #print(response.status_code)
            #print(str(response.text))
            if response.status_code == 200:
                result, decode_json = UnPackData.get_json_format(response.text)
                if result == False:
                    return {}
                return decode_json
            else:
                return {}
        except Exception, e:
            logger.error("error: %s" % str(e))

    def doPostJsonForString(self, strUrl, mParam, mHeaders = None):
        resp_flag = True
        try:
            if checkParam(strUrl) == False and checkParam(mParam) == False:
                return{}
            headers = {}
            if mHeaders == None:
                headers['content-type'] = 'application/json'
            else:
                headers['app_key'] = mHeaders['app_key']
                headers['Authorization'] = mHeaders['Authorization']
                headers['content-type'] = 'application/json'

            response = self.httpsclient.post(url=strUrl, data=json.dumps(mParam), headers=headers)
            #logger.error(response.status_code)
            #logger.error(str(response.text))

            if response.status_code != 200 and response.status_code != 201:
                resp_flag = False
                logger.error(response.status_code)
                logger.error(str(response.text))
            if len(response.text) > 0:
                result, decode_json = UnPackData.get_json_format(response.text)
                if result == False:
                    return result, {}
                return result, decode_json
            else:
                return resp_flag, {}
        except Exception, e:
            logger.error("error: %s" % str(e))
            return resp_flag, {}

    def authentication(self):
        strUrlLogin = self.mStrBaseUrl + "iocm/app/sec/v1.1.0/login"
        params = {}
        params["appId"] = self.mStrAppId
        params["secret"] = self.mStrPassword
        # Authentication
        mResult = self.doPostFormUrlEncodedForString(strUrlLogin, params)
        #print(str(mResult))
        if len(mResult) > 0:
            self.mMapHeader['app_key'] = self.mStrAppId
            self.mMapHeader['Authorization'] = str(mResult.get("tokenType")) + ' ' + str(mResult.get("accessToken"))
            self.mStrRefreshToken = str(mResult.get("refreshToken"))
            self.mRefreshTokenTimeout = int(mResult.get("expiresIn"))
            logger.info("authentication successed!.")
            return True
        else:
            logger.error("authentication Failed, Check AppID and Password.")
            return False


    def refreshToken(self):
        current_time = dt_date_time.now()
        if (current_time - self.mAuthTime).seconds < self.mRefreshTokenTimeout/2:
            return False, ' '
        self.mAuthTime = current_time
        strUrl = self.mStrBaseUrl + "iocm/app/sec/v1.1.0/refreshToken"
        params = {}
        params["appId"] = self.mStrAppId
        params["secret"] = self.mStrPassword
        params["refreshToken"] = self.mStrRefreshToken
        # refreshToken
        result, strResponse = self.doPostJsonForString(strUrl, params)
        if result == True:
            self.mMapHeader['app_key'] = self.mStrAppId
            self.mMapHeader['Authorization'] = str(strResponse.get("tokenType")) + ' ' + str(strResponse.get("accessToken"))
            self.mStrRefreshToken = str(strResponse.get("refreshToken"))
            self.mRefreshTokenTimeout = int(strResponse.get("expiresIn"))
            logger.info("refreshToken successed!.")
            return True,  ' '
        else:

            logger.error("refreshToken Failed, Check AppID and Password.")
            return False, str(strResponse.get("error_code", ' '))

    def postAsyncCommand(self, strDeviceId, strServiceId, strCommand, strItem, strValue, strTimeout):
        if checkParam(strDeviceId) == False \
                and checkParam(strServiceId) == False \
                and checkParam(strCommand) == False \
                and checkParam(strItem) == False \
                and checkParam(strValue) == False \
                and checkParam(strTimeout) == False:
            logger.error("param is null.")
            return False, ' '

        strUrl = self.mStrBaseUrl + "iocm/app/cmd/v1.4.0/deviceCommands"
        rawDataParam = {}
        rawDataParam[strItem] = strValue

        paramCommand = {}
        paramCommand['serviceId'] = strServiceId
        paramCommand['method'] = strCommand
        paramCommand['paras'] = rawDataParam

        paramPostAsynCmd = {}
        paramPostAsynCmd['deviceId'] = strDeviceId
        paramPostAsynCmd['command'] = paramCommand

        result, strResponse = self.doPostJsonForString(strUrl, paramPostAsynCmd, self.mMapHeader)
        if result == True:
            return True, ' '
        else:
            if strResponse.get("resultcode", '') == '1010005':
                self.refreshToken()
            logger.error("%s,%s, %s error" % (str(strUrl), str(paramPostAsynCmd), str(self.mMapHeader)))
            return False, str(strResponse.get("error_code", ' '))

    def postDevGroupCommand(self, groupList, appId, strServiceId, strCommand, strItem, strValue, strTimeout):
        if checkParam(groupList) == False \
                and checkParam(appId) == False \
                and checkParam(strServiceId) == False \
                and checkParam(strCommand) == False \
                and checkParam(strItem) == False \
                and checkParam(strValue) == False \
                and checkParam(strTimeout) == False:
            logger.error("param is null.")
            return False, ' '

        strUrl = self.mStrBaseUrl + "iocm/app/batchtask/v1.1.0/tasks"
        rawDataParam = {}
        rawDataParam[strItem] = strValue

        paramCommand = {}
        paramCommand['serviceId'] = strServiceId
        paramCommand['method'] = strCommand
        paramCommand['paras'] = rawDataParam

        taskParam = {}
        taskParam['type'] = 'GroupList'
        taskParam['groupList'] = groupList
        taskParam['command'] = paramCommand
        taskParam['maxRetransmit'] = 2

        paramPostGroupnCmd = {}
        paramPostGroupnCmd['appId'] = appId
        paramPostGroupnCmd['timeout'] = 60
        paramPostGroupnCmd['taskName'] = "DevGroupCommand%s" % dt_date_time.now().strftime('%Y%m%d%H%M%S')
        paramPostGroupnCmd['taskType'] = 'DeviceCmd'
        paramPostGroupnCmd['param'] = taskParam

        result, strResponse = self.doPostJsonForString(strUrl, paramPostGroupnCmd, self.mMapHeader)
        if result == True:
            return True, ' '
        else:
            logger.error("%s,%s, %s error" % (str(strUrl), str(paramPostGroupnCmd), str(self.mMapHeader)))
            return False, str(strResponse.get("error_code", ' '))

    def postDevListCommand(self, DevList, appId, strServiceId, strCommand, strItem, strValue, strTimeout):
        if checkParam(DevList) == False \
                and checkParam(appId) == False \
                and checkParam(strServiceId) == False \
                and checkParam(strCommand) == False \
                and checkParam(strItem) == False \
                and checkParam(strValue) == False \
                and checkParam(strTimeout) == False:
            logger.error("param is null.")
            return False, ' '

        strUrl = self.mStrBaseUrl + "iocm/app/batchtask/v1.1.0/tasks"
        rawDataParam = {}
        rawDataParam[strItem] = strValue

        paramCommand = {}
        paramCommand['serviceId'] = strServiceId
        paramCommand['method'] = strCommand
        paramCommand['paras'] = rawDataParam

        taskParam = {}
        taskParam['type'] = 'DeviceList'
        taskParam['deviceList'] = DevList
        taskParam['command'] = paramCommand
        taskParam['maxRetransmit'] = 2

        paramPostGroupnCmd = {}
        paramPostGroupnCmd['appId'] = appId
        paramPostGroupnCmd['timeout'] = 60
        paramPostGroupnCmd['taskName'] = "DevListCommand%s" % dt_date_time.now().strftime('%Y%m%d%H%M%S')
        paramPostGroupnCmd['taskType'] = 'DeviceCmd'
        paramPostGroupnCmd['param'] = taskParam

        result, strResponse = self.doPostJsonForString(strUrl, paramPostGroupnCmd, self.mMapHeader)
        if result == True:
            return True, ' '
        else:
            logger.error("%s,%s, %s error" % (str(strUrl), str(paramPostGroupnCmd), str(self.mMapHeader)))
            return False, str(strResponse.get("error_code", ' '))

    def CreateDeviceCmdCancelTaskV4(self, strDeviceId, strTimeout):
        if checkParam(strDeviceId) == False \
                and checkParam(strTimeout) == False:
            logger.error("param is null.")
            return False, ' '

        strUrl = self.mStrBaseUrl + "iocm/app/cmd/v1.4.0/deviceCommandCancelTasks"

        paraCreateDeviceCmdCancelTask = {}
        paraCreateDeviceCmdCancelTask['deviceId'] = strDeviceId

        result, strResponse = self.doPostJsonForString(strUrl, paraCreateDeviceCmdCancelTask, self.mMapHeader)
        if result == True:
            return True, ' '
        else:
            logger.error("%s,%s, %s error" % (str(strUrl), str(paraCreateDeviceCmdCancelTask), str(self.mMapHeader)))
            return False, str(strResponse.get("error_code", ' '))

    def CreateNewDirectDevice(self, verifyCode, nodeId, timeout):
        if checkParam(nodeId) == False \
                and checkParam(verifyCode) == False:
            logger.error("param is null.")
            return False, ' '

        strUrl = self.mStrBaseUrl + "iocm/app/reg/v1.2.0/devices"

        strRequest = {}
        strRequest['verifyCode'] = str(verifyCode)
        strRequest['nodeId'] = str(nodeId)
        strRequest['timeout'] = timeout
        result, strResponse = self.doPostJsonForString(strUrl, strRequest, self.mMapHeader)
        if result == True:
            return True, ' '
        else:
            logger.error("%s,%s, %s error" % (str(strUrl), str(strRequest), str(self.mMapHeader)))
            return False, str(strResponse.get("error_code", ' '))


    def subscribe(self, strNotifyType, strCallbackUrl):
        if checkParam(strNotifyType) == False \
                and checkParam(strCallbackUrl) == False:
            logger.error("param is null.")
            return False, ' '

        strUrl = self.mStrBaseUrl + "iocm/app/sub/v1.1.0/subscribe"

        paramSubscribe = {}
        paramSubscribe['notifyType'] = strNotifyType.encode("utf-8")
        paramSubscribe['callbackurl'] = strCallbackUrl.encode("utf-8")
        logger.info("%s,%s, %s" % (str(strUrl), str(paramSubscribe), str(self.mMapHeader)))
        result, strResponse = self.doPostJsonForString(strUrl, paramSubscribe, self.mMapHeader)
        if result == True:
            return True, ' '
        else:
            logger.error("%s,%s, %s error" % (str(strUrl), str(paramSubscribe), str(self.mMapHeader)))
            return False, str(strResponse.get("error_code", ' '))

    def CreateNewDevGroup(self, groupName, appID, deviceIds):
        if checkParam(groupName) == False \
                and checkParam(appID) == False \
                and checkParam(deviceIds) == False:
            logger.error("param is null.")
            return False, ' '

        strUrl = self.mStrBaseUrl + "iocm/app/devgroup/v1.3.0/devGroups"

        strRequest = {}
        strRequest['name'] = str(groupName)
        strRequest['appID'] = str(appID)
        strRequest['deviceIds'] = str(deviceIds)
        result, strResponse = self.doPostJsonForString(strUrl, strRequest, self.mMapHeader)
        if result == True:
            return True, strResponse
        else:
            logger.error("%s,%s, %s error" % (str(strUrl), str(strRequest), str(self.mMapHeader)))
            return False, str(strResponse.get("error_code", ' '))


    def DeleteDevGroup(self, devGroupId, accessAppId):
        if checkParam(devGroupId) == False \
                and checkParam(accessAppId) == False:
            logger.error("param is null.")
            return False, ' '

        strUrl = self.mStrBaseUrl + "iocm/app/devgroup/v1.3.0/devGroups/{%s}?accessAppId={%s}" \
                                    % (str(devGroupId), str(accessAppId))
        strRequest = {}
        strRequest['devGroupId'] = str(devGroupId)
        strRequest['accessAppId'] = str(accessAppId)
        result, strResponse = self.doPostJsonForString(strUrl, strRequest, self.mMapHeader)
        if result == True:
            return True, ''
        else:
            logger.error("%s,%s, %s error" % (str(strUrl), str(strRequest), str(self.mMapHeader)))
            return False, str(strResponse.get("error_code", ' '))

    def AddGroupDev(self, devGroupId, accessAppId, deviceIds):
        if checkParam(devGroupId) == False \
                and checkParam(accessAppId) == False \
                and checkParam(deviceIds) == False:
            logger.error("param is null.")
            return False, ' '

        strUrl = self.mStrBaseUrl + "iocm/app/dm/v1.1.0/devices/addDevGroupTagToDevices?accessAppId={%s}" \
                                    % (str(accessAppId))
        strRequest = {}

        strRequest['accessAppId'] = str(accessAppId)
        strRequest['devGroupId'] = str(devGroupId)
        strRequest['deviceIds'] = str(deviceIds)
        result, strResponse = self.doPostJsonForString(strUrl, strRequest, self.mMapHeader)
        if result == True:
            return True, strResponse
        else:
            logger.error("%s,%s, %s error" % (str(strUrl), str(strRequest), str(self.mMapHeader)))
            return False, str(strResponse.get("error_code", ' '))

    def DeleteGroupDev(self, devGroupId, accessAppId, deviceIds):
        if checkParam(devGroupId) == False \
                and checkParam(accessAppId) == False \
                and checkParam(deviceIds) == False:
            logger.error("param is null.")
            return False, ' '

        strUrl = self.mStrBaseUrl + "iocm/app/dm/v1.1.0/devices/deleteDevGroupTagFromDevices?accessAppId={%s}" \
                                    % (str(accessAppId))
        strRequest = {}
        strRequest['accessAppId'] = str(accessAppId)
        strRequest['devGroupId'] = str(devGroupId)
        strRequest['deviceIds'] = str(deviceIds)
        result, strResponse = self.doPostJsonForString(strUrl, strRequest, self.mMapHeader)
        if result == True:
            return True, ''
        else:
            logger.error("%s,%s, %s error" % (str(strUrl), str(strRequest), str(self.mMapHeader)))
            return False, str(strResponse.get("error_code", ' '))

    def nbiot_sendCommand(self, strDeviceId, commandData):
        try:
            self.nbiot_sendlock.acquire()
            sendCommand = base64.b64encode(commandData)
            bRet, response = self.postAsyncCommand(strDeviceId, 'CommandSend', 'CommandSend', 'rawData', sendCommand, '120')
            self.nbiot_sendlock.release()
            if bRet == False:
                #if response == '100223':
                #    self.CreateDeviceCmdCancelTaskV4(strDeviceId, '0')
                logger.error("postAsyncCommand Failed, Check DeviceId is register.")
                return
            logger.info("postAsyncCommand successed!.")
            return
        except Exception, e:
            logger.error("error: %s" % str(e))
            logger.error("strDeviceId: %s , commandData %s" % (str(strDeviceId), str(commandData)))

    def nbiot_senddCommand_fordevlist(self, DeviceIdlist, commandData):
        try:
            self.nbiot_sendlock.acquire()
            sendCommand = base64.b64encode(commandData)

            bRet, response = self.postDevListCommand(DeviceIdlist, self.mStrAppId, 'CommandSend', 'CommandSend', 'rawData', sendCommand, '120')
            self.nbiot_sendlock.release()
            if bRet == False:
                #if response == '100223':
                #    self.CreateDeviceCmdCancelTaskV4(strDeviceId, '0')
                logger.error("nbiot_senddCommand_fordevlist Failed, Check DeviceId is register.")
                return
            logger.info("nbiot_senddCommand_fordevlist successed!.")
            return
        except Exception, e:
            logger.error("error: %s" % str(e))
            logger.error("strDeviceId: %s , commandData %s" % (str(DeviceIdlist), str(commandData)))

    def nbiot_server_subscribe(self):
        try:
            devDataChangeCB = SERVER_SUB_ADDRESS_BASE + ':' + SERVER_SUB_PORT + NBIOT_DEVICE_DATA_CHANGED_CALLBACK_URL
            bRet, response = self.subscribe('deviceDataChanged', devDataChangeCB)
            if bRet == False:
                logger.error("nbiot_server_subscribe Failed, Check DeviceId is register.")
                return
            '''
            addDevCB = SERVER_SUB_ADDRESS_BASE + ':' + SERVER_SUB_PORT + NBIOT_ADD_DEVICE_CALLBACK_URL
            bRet, response = self.subscribe('deviceAdded', addDevCB)
            if bRet == False:
                logger.error("nbiot_server_subscribe Failed, Check DeviceId is register.")
                return
            '''
            logger.info("nbiot_server_subscribe successed!.")
            return
        except Exception, e:
            logger.error("error: %s" % str(e))
